#include "uBroadcast_B1.h"

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>

static int readChar(int fd) {
	char c = 0;
	int tmp = read(fd, &c, 1);
	if (tmp != 1) {
		if (tmp < 0) {
			perror("Serial BugOne read error");
		}
		return tmp;
	}
	return c;
}

ub_B1 * ub_b1_connect(char *device) {
	// --- Open port ---
	int fd = open(device, O_RDWR | O_NOCTTY);
	if (fd == -1) {
		perror("Unable to open BugOne sniffer port");
		return NULL;
	}

	// --- Set port options ---
	struct termios toptions;

	if (tcgetattr(fd, &toptions) < 0) {
		perror("Couldn't get serial port attributes");
		close(fd);
		return NULL;
	}
	cfsetispeed(&toptions, B38400);
	cfsetospeed(&toptions, B38400);

	// 8N1
	toptions.c_cflag &= ~PARENB;
	toptions.c_cflag &= ~CSTOPB;
	toptions.c_cflag &= ~CSIZE;
	toptions.c_cflag |= CS8;
	// no flow control
	toptions.c_cflag &= ~CRTSCTS;

	toptions.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
	toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl

	toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
	toptions.c_oflag &= ~OPOST; // make raw

	// see: http://unixwiz.net/techtips/termios-vmin-vtime.html
	toptions.c_cc[VMIN] = 0;
	toptions.c_cc[VTIME] = 20;

	if (tcsetattr(fd, TCSANOW, &toptions) < 0) {
		perror("Couldn't set serial port attributes");
		close(fd);
		return NULL;
	}

	ub_B1 * conn = (ub_B1 *) malloc(sizeof(ub_B1));
	if (!conn) {
		close(fd);
		return NULL;
	}
	conn->serialPort = fd;
	return conn;
}

void ub_b1_send(ub_B1 * conn, uMessage * msg) {
	if (!conn) {
		perror("NULL BugOne connection");
		return;
	}

	char * ptr = msg->buffer;
	int size = msg->buffer[1] + 3;
	while (size) {
		int transmitted = write(conn->serialPort, ptr, size);
		if (transmitted <= 0) {
			perror("BugOne transmission");
			return;
		}
		ptr += transmitted;
		size -= transmitted;
	}
	return ;
}

int ub_b1_recv(ub_B1 * conn, uMessage * msg) {
	if (!conn) {
		perror("NULL BugOne connection");
		return -1;
	}

	// Clear message
	ub_clear(msg);

	// Wait for type + size
	int type = readChar(conn->serialPort);
	if (type < 0) {
		return -1;
	}
	UB_TYPE(msg) = type;

	int size = readChar(conn->serialPort);
	if (size < 0) {
		ub_clear(msg);
		return -1;
	}
	UB_SIZE(msg) = size;

	for (; size >= 0; size--) {
		int tmp = readChar(conn->serialPort);
		if (tmp < 0) {
			ub_clear(msg);
			return -1;
		}
		*msg->ptr++ = tmp;
	}
	msg->checksum = *(msg->ptr + 1);

	if (!ub_check(msg)) {
		ub_clear(msg);
		// Don't care, just throw it away
		// perror("Invalid message received");
		return -3;
	}

	return 0;
}

void ub_b1_disconnect(ub_B1 * conn) {
	if (conn) {
		close(conn->serialPort);
		free(conn);
	}
}
